package com.chuisha;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.json.JSONUtil;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;


import java.time.LocalDateTime;
import java.util.Date;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static cn.hutool.core.date.DatePattern.NORM_DATETIME_MS_FORMAT;

/**
 * @Description: 请求日志
 */
@Aspect
@Component
public class WebLogAspect {

    private static final Logger log = LoggerFactory.getLogger("accessDataLog");

    @Pointcut("execution(public * com.chuisha.controller.*.*(..))")
    public void weblog() {
    }

    @Around("weblog()")
    public Object doRound(ProceedingJoinPoint joinPoint) {
        StringBuilder msg = new StringBuilder();
        Date reqTime = new Date();
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();

            // 可在此处扩展，从request中获取traceId，串联整个链路
            MDC.put(ThreadMDCUtil.TRACE_ID, ThreadMDCUtil.generatorTraceId());
            msg.append(" remote ip:");
            msg.append(request.getRemoteAddr());
            msg.append(" request url:");
            msg.append(request.getRequestURL().toString());
            msg.append(" param:");
            msg.append(JSONUtil.toJsonStr(getParams(joinPoint)));
            Object result = joinPoint.proceed();
            msg.append(" resp:");
            msg.append(JSONUtil.toJsonStr(result));
            return result;
        } catch (Throwable e) {
            log.error("request aop error", e);
            msg.append(" resp: error:").append(e.getMessage());
            throw new RuntimeException(e);
        } finally {
            msg.append(" reqTime:").append(DateUtil.format(reqTime, NORM_DATETIME_MS_FORMAT));
            msg.append(" respTime:").append(DateUtil.format(new Date(), NORM_DATETIME_MS_FORMAT));
            msg.append(" cost:").append(System.currentTimeMillis() - reqTime.getTime());
            log.info(msg.toString());
            MDC.clear();
        }

    }

    private Object getParams(JoinPoint joinPoint) {
        return Stream.of(joinPoint.getArgs())
                .filter(Objects::nonNull)
                .filter(o -> !(o instanceof ServletRequest))
                .filter(o -> !(o instanceof ServletResponse))
                .map(o -> {
                    if (o instanceof MultipartFile) {
                        return ((MultipartFile) o).getOriginalFilename();
                    }
                    return o;
                })
                .collect(Collectors.toList());
    }

}
